Skip to content

sync 0.32.0 - #7

Merged
tstirrat15 merged 41 commits into
masterfrom
sync-upstream-v0.32.0
Aug 19, 2026
Merged

sync 0.32.0#7
tstirrat15 merged 41 commits into
masterfrom
sync-upstream-v0.32.0

Conversation

@miparnisari

@miparnisari miparnisari commented Aug 19, 2026

Copy link
Copy Markdown

lopster568 and others added 30 commits July 27, 2026 10:37
nativeTypeProvider.NativeToValue routed every struct-kind value to
newNativeObject, regardless of whether the type was registered via
ext.NativeTypes. Every other method on the provider checks the
nativeTypes registry first and falls back to the composed base adapter
when a type is not registered; NativeToValue was the exception, so a
custom adapter added with cel.CustomTypeAdapter never saw unregistered
structs it wanted to convert itself.

Mirror the registry check from NewValue: only wrap registered struct
types as native objects, and delegate the rest to the base adapter.

Fixes cel-expr#1343
* Regex program plan size controls

This implementation mirrors the logic used in RE2 within cel-java
and cel-cpp. Regex implementations across platforms do not guarantee
equivalent program plan sizes, but this at least provides a means
to control the plan size per-platform.

* Minor refactor for maintenance of limit extraction

* Minor updates to test setup
* Simplify support for native object types

Shift most of the logic from the NativeTypeProvider over to the NativeObject
wrappers. This change is a stepping stone to consolidating type providers
into the common/types package and making it less painful to manage type
adaptation.

* Fix for embedded type field traversal

* Robustness checks for field retrieval with nil embedded pointers to structs

Since promoted fields were previously not discovered, field setting
also needed to be updated to ensure nested pointers to structs with
promoted fields were initialized properly.

* Note for future work
* Support self-describing, self-adapting struct types
* Safer duplicate type detection
* Optimize NativeToValue to minimize allocs

- **Unified `NativeToValue` Dispatch**: Consolidated private `nativeToValue` and `Registry.NativeToValue` into a single authoritative receiver method. `DefaultTypeAdapter` delegates directly to `emptyRegistry.NativeToValue`.
- **Reflection & Allocation Reduction**:
  - Replaced dynamic `.Convert().Interface().(T)` reflection conversions for type aliases with direct zero-allocation primitive getters (`.Int()`, `.Uint()`, `.Float()`, `.Bool()`, `.String()`).
  - Integrated custom struct descriptor lookups directly into the reflection branch using `reflect.TypeOf(value)` for instant map matching.
  - Eliminated duplicate `reflect.ValueOf()` instantiations and redundant nil pointer checks across conversion paths.
---

| Benchmark Target | `master` Baseline | Refactored Branch | Delta | Allocs / Op |
| :--- | :---: | :---: | :---: | :---: |
| `proto/TestAllTypes` | 177.0 ns/op | **87.91 ns/op** | 🚀 **+50.3% faster** | 1 alloc |
| `nativeStruct/pointer` | 20.59 ns/op | **13.18 ns/op** | 🚀 **+36.0% faster** | 0 allocs |
| `nativeStruct/value` | 17.25 ns/op | **13.52 ns/op** | 🚀 **+21.6% faster** | 0 allocs |
| `ref.Val/String` | 4.81 ns/op | **2.62 ns/op** | 🚀 **+45.5% faster** | 0 allocs |
| `ref.Val/Int` | 3.73 ns/op | **2.69 ns/op** | 🚀 **+27.9% faster** | 0 allocs |
| `int/1` | 3.86 ns/op | **2.55 ns/op** | 🚀 **+33.9% faster** | 0 allocs |
| `bool/true` | 2.91 ns/op | **2.11 ns/op** | 🚀 **+27.5% faster** | 0 allocs |

| Benchmark Target | `master` Baseline | Refactored Branch | Delta |
| :--- | :---: | :---: | :---: |
| `nested_proto_field` | 141.3 ns/op | **124.7 ns/op** | 🚀 **+11.7% faster** |
| `nested_proto_field_with_index` | 577.5 ns/op | **540.6 ns/op** | 🚀 **+6.4% faster** |
| `index` | 122.3 ns/op | **108.1 ns/op** | 🚀 **+11.6% faster** |
| `index_list_int_uint_type_index` | 42.87 ns/op | **38.22 ns/op** | 🚀 **+10.8% faster** |
| `index_cross_type_float_uint` | 264.4 ns/op | **240.0 ns/op** | 🚀 **+9.2% faster** |
| `select_subsumed_field` | 14.27 ns/op | **13.05 ns/op** | 🚀 **+8.5% faster** |
| `select_custom_pb3_optional_field` | 43.96 ns/op | **41.71 ns/op** | 🚀 **+5.1% faster** |
| `complex_qual_vars` | 338.4 ns/op | **323.1 ns/op** | 🚀 **+4.5% faster** |

* Consistency fix for safe field getter

* Remove test added by mistake
* Program plan optimizations

| Benchmark Case | Before (ns/op) | After (ns/op) | Δ Time | Before (B/op) | After (B/op) | Δ Memory | Before (allocs) | After (allocs) | Δ Allocs |
| :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| BenchmarkProgramPlan/Default | 8,153 | 930 | **-88.6%** | 8,320 | 1,416 | **-83.0%** | 36 | 26 | **-27.8%** |
| BenchmarkProgramPlan/OptimizeUnneeded | 7,344 | 1,150 | **-84.3%** | 8,784 | 1,512 | **-82.8%** | 50 | 32 | **-36.0%** |
| BenchmarkProgramPlan/OptimizeNeeded | 8,370 | 2,164 | **-74.1%** | 10,224 | 2,976 | **-70.9%** | 67 | 52 | **-22.4%** |

* Minor refactor of the initialization logic to reduce program size

* Shift the dispatcher-reuse to the environment rather than the program
* Fold list concat expressions together
* Test cases for optional tracking
* Program plan optimizations

| Benchmark Case | Before (ns/op) | After (ns/op) | Δ Time | Before (B/op) | After (B/op) | Δ Memory | Before (allocs) | After (allocs) | Δ Allocs |
| :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| BenchmarkProgramPlan/Default | 8,153 | 930 | **-88.6%** | 8,320 | 1,416 | **-83.0%** | 36 | 26 | **-27.8%** |
| BenchmarkProgramPlan/OptimizeUnneeded | 7,344 | 1,150 | **-84.3%** | 8,784 | 1,512 | **-82.8%** | 50 | 32 | **-36.0%** |
| BenchmarkProgramPlan/OptimizeNeeded | 8,370 | 2,164 | **-74.1%** | 10,224 | 2,976 | **-70.9%** | 67 | 52 | **-22.4%** |

* Minor refactor of the initialization logic to reduce program size

* Copy-on-write semantics for types.Registry and cel.Env internals

* Ensure shared declarations aren't copied unless necessary within the checker

* Capture NewEnv setup benchmarks as well

* Fix race-related issue with copy-on-write mutability check

* Eliminate dead-code from former Copy() approach. More tests

* Bug fix to support disabling declarations when using inherited declarations
Following the repository relocation and adoption of the cel.dev domain,
update the module path in go.mod files from github.com/google/cel-go to
cel.dev/cel-go. Update all internal package imports and Bazel BUILD
definitions accordingly.
…amp (cel-expr#1414)

* Parsing helper for working with different types and formats of timestamp
* Add overflow checks and negative tests
* Add Go-native JSON type support into NativeToValue
* JWT object rep and helpers for inspecting claims
* JWT data types, parse, and claim helpers
…-expr#1404)

* Support aggregate size computations over list, maps, and structs
* Depth and traversal limits for aggregate size calculations
* Enhancements to use Foldable and reduce duplication in native size computations
Cost tracking silently reported a cost of zero whenever state tracking was also
enabled:

	cel.CostTracking(nil)                                 -> 5
	cel.CostTracking(nil) + cel.EvalOptions(OptTrackState)   -> 0
	cel.CostTracking(nil) + cel.EvalOptions(OptExhaustiveEval) -> 0

Each observer installed its own decorator, and decObserveEval returns a node
which is already wrapped in a watcher untouched. Since the planner applies
decorators in order, the state observer's decorator wrapped each node first and
the cost observer's decorator then found a watcher and left it alone, so the
cost observer's per-node callback was never installed. ObservableInterpretable
still ran the tracker's InitState and GetState, so evaluation produced a
CostTracker reporting a cost of zero rather than an error or a nil result --
including for programs configured with a cost limit, which then could not be
exceeded.

Observers now register only as observers, and the planner installs a single
decorator which reports each observation to all of them.
String and bytes values now count as one element per ten bytes, rounding up
with a minimum of one element, configurable via SizeCalculatorStringUnitLength.
Sizes are measured in bytes rather than characters so that sizing large values
is O(1) rather than a full UTF-8 scan per observation; byte length is never
smaller than the character count, so byte-based sizing is conservative for
limit enforcement.
* Aggregate policy evaluation semantics support
* Additional aggregation tests showcasing multiple aggregation rules with nested match behavior
* Replace 'emit' with 'output' to conform to spec update
* Updates to the composition base step and aggregation
The same saturating arithmetic existed in four places under three names:
`safeAdd`/`safeMul` in `interpreter`, byte-identical copies in `ext`, and
`addUint64NoOverflow`/`multiplyUint64NoOverflow`/`multiplyByCostFactor` in
`checker`. Costs and sizes are uint64 values where math.MaxUint64 doubles as
"unbounded", so every operation on them has to saturate rather than wrap;
having one implementation of that per package made it easy for the domains to
drift.

`common/cost` now owns the single exported set: SafeAdd, SafeMultiply,
SafeMultiplyByFactor, and SafeCeil. The package depends on nothing but `math`,
so `checker`, `interpreter`, and `ext` can all reach it without cycles.

Behavioral notes, all in extreme-value territory:

- `CostTracker.costCall` computed the `matches` and `contains` products with an
  unguarded `*` while `safeMul` sat unused in the same file. Those products now
  saturate instead of wrapping, which matters as soon as an operand size can
  itself be saturated.
- Inline `uint64(math.Ceil(float64(x)*factor))` conversions became
  SafeMultiplyByFactor, which is identical in range and returns MaxUint64
  rather than an implementation-defined value out of range.
- Truncating conversions (`uint64(float64(size)*costFactor)`) were left alone
  so that no in-range cost changes value.

Locals named `cost` in the touched functions were renamed to `total`/`estimate`
so the package identifier is not shadowed.
TristonianJones and others added 11 commits August 18, 2026 14:39
The aggregate size memoization on immutable lists and maps is now accessed
atomically since values may be shared across concurrent evaluations, and
sizes from computations aborted at the calculator's depth or traversal
limits are no longer memoized, as they depend on where in the traversal the
value was encountered. EstimateAggregateSize additionally distinguishes
genuine uint32 saturation from computations aborted at the limits.
Switch module and import paths to cel.dev/cel-go
@tstirrat15
tstirrat15 merged commit e04836b into master Aug 19, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants